<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // 函数声明, 重复声明会被覆盖
    f1();
    function f1(index, name, age) {
        // name属性获取函数名字
        console.log(f1.name);
        // length获取参数个数
        console.log(f1.length);
    }
    // 当函数使用语句声明时，不能先调用后声明 (TypeError)，其他声明方式可以
    // f(); //  TypeError: f is not a function
    var f = function f() {
        console.log(f);
    }; // 常用用法习惯
    var other = function () {

    }; //
    other(); // 函数调用
    // 函数递归调用，斐波那契，计算第n+1个数的值（索引为n）
    console.log(feibonaqiArray(3));
    function feibonaqiArray(num) {
        if (num === 0) {return 0}
        if (num === 1) {return 1}
        return feibonaqiArray(num - 2) + feibonaqiArray(num - 1);
    }
    // 函数的声明只能全局声明或者在函数内部声明，建议不要在其他代码块内声明
    // 结果可能运行，可能无效或出错
    if (2) {
        function f2() {
            console.log(f2);
        }
        f2();
    }

    // 函数作用域，是定义时的作用域，非运行时的作用域
    var printMsg = function (num) {
        console.log(a);
    };
    function mainPrint() {
        var a = "abc";
        // printMsg(a); // ReferenceError: a is not defined
    }
    mainPrint();
    // 函数实参传入的个数不需等于形参，可多可少
    // 若参数同同名，取最后一个
    function addNumber(a, b) {
        console.log(arguments);
        // 非严格模式下，可对传入的参数进行修改
        arguments[1] = 3;
        console.log(arguments["callee"]); // callee属性返回原函数
        return a + b;
    }
    // arguments对象，只在函数内部使用，包含了所有的函数参数

    console.log(addNumber(1));
    // 值传递与引用传递（传址传递）：
    // 对象（非5种基本类型）为引用传递，当只改变对象内部时，原始值受影响，重新赋值，原始值不受影响

    // 闭包：定义在函数内部的函数
    // 作用：
    // 可以使用父级函数的属性（变量/方法）
    // 使得变量始终保存在内存中（环境一直存在），保存上一次调用的结果
    // 每一次运行外部函数会产生新的闭包，而闭包又保留外层函数的变量，耗内存
    // 封装属性和方法
    function power(x) {
        return function () {
            return x++;
        };
    }
    var a = power(3);
    console.log(a()); // 3
    console.log(a()); // 4
    console.log(a()); // 5

    // 立即调用的函数表达式，使用" () ",但function关键字不能放在句首，应用括号括起来
    // 作用：形成一个单独的作用域（私有变量外部无法读取），避免污染全局变量。
    (function () {
        console.log(Date());
    })(); // 表示立即执行该函数，此时该句为一个语句，应使用";"。
    // 或者如下：
    (function () {
        console.log(Date());
    }());

    // eval函数： 可以执行字符串中的语句
    var apple = "apple";
    function f10() {
        var apple = "orange";
        eval("apple = 'pear'");
        console.log(apple);
    }
    f10();

    var a = 1;
    function f11() {
        var a = 2;
        var e = eval;
        e('console.log(a)');  // 1
        eval('console.log(a)');  // 2
    }
    f11() // 1
</script>
</body>
</html>